import { resolve, basename, extname } from 'node:path';
import { writeFile } from 'node:fs/promises';

import { readFile, isWindows, styleText } from '../common.js';
import { $init, tools } from '../../obj/wasm-tools.js';
const {
    print: printFn,
    parse: parseFn,
    componentWit: componentWitFn,
    componentNew: componentNewFn,
    componentEmbed: componentEmbedFn,
    metadataAdd: metadataAddFn,
    metadataShow: metadataShowFn,
} = tools;

export async function parse(file, opts) {
    await $init;
    const source = (await readFile(file)).toString();
    const output = parseFn(source);
    await writeFile(opts.output, output);
}

export async function print(file, opts) {
    await $init;
    const source = await readFile(file);
    const output = printFn(source);
    if (opts.output) {
        await writeFile(opts.output, output);
    } else {
        console.log(output);
    }
}

export async function componentWit(file, opts) {
    await $init;
    const source = await readFile(file);
    const output = componentWitFn(source, opts.document);
    if (opts.output) {
        await writeFile(opts.output, output);
    } else {
        console.log(output);
    }
}

export async function componentNew(file, opts) {
    await $init;
    const source = file ? await readFile(file) : null;
    let adapters = [];
    if (opts.wasiReactor && opts.wasiCommand) {
        throw new Error('Must select one of --wasi-command or --wasi-reactor');
    }
    if (opts.wasiReactor) {
        adapters = [
            [
                'wasi_snapshot_preview1',
                (
                    await readFile(
                        new URL(
                            '../../lib/wasi_snapshot_preview1.reactor.wasm',
                            import.meta.url
                        )
                    )
                ).buffer,
            ],
        ];
    } else if (opts.wasiCommand) {
        adapters = [
            [
                'wasi_snapshot_preview1',
                (
                    await readFile(
                        new URL(
                            '../../lib/wasi_snapshot_preview1.command.wasm',
                            import.meta.url
                        )
                    )
                ).buffer,
            ],
        ];
    }
    if (opts.adapt) {
        adapters = adapters.concat(
            await Promise.all(
                opts.adapt.map(async (adapt) => {
                    let adapter;
                    if (adapt.includes('=')) {
                        adapter = adapt.split('=');
                    } else {
                        adapter = [
                            basename(adapt).slice(0, -extname(adapt).length),
                            adapt,
                        ];
                    }
                    adapter[1] = await readFile(adapter[1]);
                    return adapter;
                })
            )
        );
    }
    const output = componentNewFn(source, adapters);
    await writeFile(opts.output, output);
}

export async function componentEmbed(file, opts) {
    await $init;
    if (opts.metadata) {
        opts.metadata = opts.metadata.map((meta) => {
            const [field, data = ''] = meta.split('=');
            const [name, version = ''] = data.split('@');
            return [field, [[name, version]]];
        });
    }
    const source = file ? await readFile(file) : null;
    opts.binary = source;
    opts.witPath = (isWindows ? '//?/' : '') + resolve(opts.wit);
    const output = componentEmbedFn(opts);
    await writeFile(opts.output, output);
}

export async function metadataAdd(file, opts) {
    await $init;
    const metadata = opts.metadata.map((meta) => {
        const [field, data = ''] = meta.split('=');
        const [name, version = ''] = data.split('@');
        return [field, [[name, version]]];
    });
    const source = await readFile(file);
    const output = metadataAddFn(source, metadata);
    await writeFile(opts.output, output);
}

export async function metadataShow(file, opts) {
    await $init;
    const source = await readFile(file);
    let output = '',
        stack = [1];
    const meta = metadataShowFn(source);
    if (opts.json) {
        console.log(JSON.stringify(meta, null, 2));
    } else {
        for (const { name, metaType, producers } of meta) {
            output += '  '.repeat(stack.length - 1);
            const indent = '  '.repeat(stack.length);
            if (metaType.tag === 'component') {
                output += `${styleText('bold', `[component${name ? ' ' + name : ''}]`)}\n`;
                if (metaType.val > 0) {
                    stack.push(metaType.val);
                }
            } else {
                output += `${styleText('bold', `[module${name ? ' ' + name : ''}]`)}\n`;
            }
            if (producers.length === 0) {
                output += `${indent}(no metadata)\n`;
            }
            for (const [field, items] of producers) {
                for (const [name, version] of items) {
                    output += `${indent}${(field + ':').padEnd(13, ' ')} ${name}${version ? `${styleText('cyan', version)}` : ''}\n`;
                }
            }
            output += '\n';
            if (stack[stack.length - 1] === 0) {
                stack.pop();
            }
            stack[stack.length - 1]--;
        }
        process.stdout.write(output);
    }
}
